home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / share / hplip / scan / sane.py < prev   
Text File  |  2008-10-13  |  22KB  |  604 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # (c) Copyright 2003-2007 Hewlett-Packard Development Company, L.P.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18. #
  19. # Based on:
  20. # "sane.py", part of the Python Imaging Library (PIL)
  21. # http://www.pythonware.com/products/pil/
  22. # Python wrapper on top of the _sane module, which is in turn a very
  23. # thin wrapper on top of the SANE library.  For a complete understanding
  24. # of SANE, consult the documentation at the SANE home page:
  25. # http://www.mostang.com/sane/ .#
  26. #
  27. # Modified to work without PIL by Don Welch
  28. # (C) Copyright 2003 A.M. Kuchling.  All Rights Reserved
  29. # (C) Copyright 2004 A.M. Kuchling, Ralph Heinkel  All Rights Reserved
  30. #
  31. # Permission to use, copy, modify, and distribute this software and its
  32. # documentation for any purpose and without fee is hereby granted,
  33. # provided that the above copyright notice appear in all copies and that
  34. # both that copyright notice and this permission notice appear in
  35. # supporting documentation, and that the name of A.M. Kuchling and
  36. # Ralph Heinkel not be used in advertising or publicity pertaining to 
  37. # distribution of the software without specific, written prior permission.
  38. # A.M. KUCHLING, R.H. HEINKEL DISCLAIM ALL WARRANTIES WITH REGARD TO THIS 
  39. # SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
  40. # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  41. # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
  42. # USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  43. # OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  44. # PERFORMANCE OF THIS SOFTWARE.
  45. # Python wrapper on top of the scanext module, which is in turn a very
  46. # thin wrapper on top of the SANE library.  For a complete understanding
  47. # of SANE, consult the documentation at the SANE home page:
  48. # http://www.mostang.com/sane/ .
  49. #
  50. # Original authors: Andrew Kuchling, Ralph Heinkel
  51. # Modified by: Don Welch
  52. #
  53.  
  54. # Std Lib
  55. import scanext
  56. import threading
  57. import time
  58. import os
  59. import Queue
  60.  
  61. # Local
  62. from base.g import *
  63. from base import utils
  64.  
  65. EVENT_SCAN_CANCELED = 1
  66.  
  67. TYPE_STR = { scanext.TYPE_BOOL:   "TYPE_BOOL",   scanext.TYPE_INT:    "TYPE_INT",
  68.              scanext.TYPE_FIXED:  "TYPE_FIXED",  scanext.TYPE_STRING: "TYPE_STRING",
  69.              scanext.TYPE_BUTTON: "TYPE_BUTTON", scanext.TYPE_GROUP:  "TYPE_GROUP" }
  70.  
  71. UNIT_STR = { scanext.UNIT_NONE:        "UNIT_NONE",
  72.              scanext.UNIT_PIXEL:       "UNIT_PIXEL",
  73.              scanext.UNIT_BIT:         "UNIT_BIT",
  74.              scanext.UNIT_MM:          "UNIT_MM",
  75.              scanext.UNIT_DPI:         "UNIT_DPI",
  76.              scanext.UNIT_PERCENT:     "UNIT_PERCENT",
  77.              scanext.UNIT_MICROSECOND: "UNIT_MICROSECOND" }
  78.  
  79.  
  80. class Option:
  81.     """Class representing a SANE option.
  82.     Attributes:
  83.     index -- number from 0 to n, giving the option number
  84.     name -- a string uniquely identifying the option
  85.     title -- single-line string containing a title for the option
  86.     desc -- a long string describing the option; useful as a help message
  87.     type -- type of this option.  Possible values: TYPE_BOOL,
  88.             TYPE_INT, TYPE_STRING, and so forth.
  89.     unit -- units of this option.  Possible values: UNIT_NONE,
  90.             UNIT_PIXEL, etc.
  91.     size -- size of the value in bytes
  92.     cap -- capabilities available; CAP_EMULATED, CAP_SOFT_SELECT, etc.
  93.     constraint -- constraint on values.  Possible values:
  94.         None : No constraint
  95.         (min,max,step)  Integer values, from min to max, stepping by
  96.         list of integers or strings: only the listed values are allowed
  97.     """
  98.  
  99.     def __init__(self, args, cur_device):
  100.         import string
  101.         self.cur_device = cur_device
  102.         
  103.         self.index, self.name, self.title, self.desc, self.type, \
  104.             self.unit, self.size, self.cap, self.constraint = args
  105.         
  106.         if type(self.name) != type(''): 
  107.             self.name = str(self.name)
  108.             
  109.     def isActive(self):
  110.         return scanext.isOptionActive(self.cap)
  111.         
  112.     def isSettable(self):
  113.         return scanext.isOptionSettable(self.cap)
  114.         
  115.     def __repr__(self):
  116.         if self.isSettable():
  117.             settable = 'yes'
  118.         else:
  119.             settable = 'no'
  120.         
  121.         if self.isActive():
  122.             active = 'yes'
  123.             curValue = self.cur_device.getOption(self.name)
  124.         else:
  125.             active = 'no'
  126.             curValue = '<not available, inactive option>'
  127.             
  128.         
  129.         return """\nName:      %s
  130. Cur value: %s
  131. Index:     %d
  132. Title:     %s
  133. Desc:      %s
  134. Type:      %s
  135. Unit:      %s
  136. Constr:    %s
  137. isActive:    %s
  138. isSettable:  %s\n""" % (self.name, curValue,
  139.                       self.index, self.title, self.desc,
  140.                       TYPE_STR[self.type], UNIT_STR[self.unit],
  141.                       self.constraint, active, settable)
  142.         return s
  143.         
  144.     def limitAndSet(self, value):
  145.         if value is not None and self.constraint is not None:
  146.             if type(self.constraint) == type(()):
  147.                 if value < self.constraint[0]:
  148.                     value = self.constraint[0]
  149.                     log.warn("Invalid value for %s (%s < min value of %d). Using %d." % 
  150.                         (self.name, self.name, value, value))
  151.                 
  152.                 elif value > self.constraint[1]:
  153.                     value = self.constraint[1]
  154.                     log.warn("Invalid value for %s (%s > max value of %d). Using %d." % 
  155.                         (self.name, self.name, value, value))
  156.                     
  157.                 self.cur_device.setOption(self.name, value)
  158.             
  159.             elif type(self.constraint) == type([]):
  160.                 if value not in self.constraint:
  161.                     v = self.constraint[0]
  162.                     min_dist = sys.maxint
  163.                     for x in self.constraint: 
  164.                         if abs(value-x) < min_dist:
  165.                             min_dist = abs(value-x)
  166.                             v = x
  167.                             
  168.                     log.warn("Invalid value for %s (%s not in constraint list: %s). Using %d." % 
  169.                         (self.name, self.name, value, ', '.join(self.constraint), v))
  170.                         
  171.                     self.cur_device.setOption(self.name, v)
  172.         
  173.         else:
  174.             value = self.cur_device.getOption(self.name)
  175.             
  176.         return value
  177.  
  178.  
  179. ##class _SaneIterator:
  180. ##    """ intended for ADF scans.
  181. ##    """
  182. ##
  183. ##    def __init__(self, cur_device):
  184. ##        self.cur_device = cur_device
  185. ##
  186. ##    def __iter__(self):
  187. ##        return self
  188. ##
  189. ##    def __del__(self):
  190. ##        self.cur_device.cancelScan()
  191. ##
  192. ##    def next(self):
  193. ##        try:
  194. ##            self.cur_device.startScan()
  195. ##        except error, v:
  196. ##            if v == 'Document feeder out of documents':
  197. ##                raise StopIteration
  198. ##            else:
  199. ##                raise
  200. ##        return self.cur_device.performScan(1)
  201.         
  202.  
  203.  
  204.  
  205. class ScanDevice:
  206.     """Class representing a SANE device.
  207.     Methods:
  208.     startScan()    -- initiate a scan, using the current settings
  209.     cancelScan()   -- cancel an in-progress scanning operation
  210.  
  211.     Also available, but rather low-level:
  212.     getParameters() -- get the current parameter settings of the device
  213.     getOptions()    -- return a list of tuples describing all the options.
  214.  
  215.     Attributes:
  216.     optlist -- list of option names
  217.  
  218.     You can also access an option name to retrieve its value, and to
  219.     set it.  For example, if one option has a .name attribute of
  220.     imagemode, and scanner is a ScanDevice object, you can do:
  221.          print scanner.imagemode
  222.          scanner.imagemode = 'Full frame'
  223.          scanner.['imagemode'] returns the corresponding Option object.
  224.     """
  225.     
  226.     def __init__(self, dev):
  227.         self.scan_thread = None
  228.         self.dev = scanext.openDevice(dev)
  229.         self.options = {}
  230.         self.__load_options_dict()
  231.  
  232.     def __load_options_dict(self):
  233.         opts = self.options
  234.         opt_list = self.dev.getOptions()
  235.         
  236.         for t in opt_list:
  237.             o = Option(t, self)
  238.             
  239.             if o.type != scanext.TYPE_GROUP:
  240.                 opts[o.name] = o
  241.  
  242.     def setOption(self, key, value):
  243.         opts = self.options
  244.         
  245.         if key not in opts:
  246.             opts[key] = value
  247.             return
  248.         
  249.         opt = opts[key]
  250.         
  251.         if opt.type == scanext.TYPE_GROUP:
  252.             raise AttributeError, "Groups can't be set: %s" % key
  253.         
  254.         if not scanext.isOptionActive(opt.cap):
  255.             raise AttributeError, 'Inactive option: %s' % key
  256.         
  257.         if not scanext.isOptionSettable(opt.cap):
  258.             raise AttributeError, "Option can't be set by software: %s" % key
  259.         
  260.         if type(value) == int and opt.type == scanext.TYPE_FIXED:
  261.             # avoid annoying errors of backend if int is given instead float:
  262.             value = float(value)
  263.         
  264.         self.last_opt = self.dev.setOption(opt.index, value)
  265.         
  266.         # do binary AND to find if we have to reload options:
  267.         if self.last_opt & scanext.INFO_RELOAD_OPTIONS:
  268.             self.__load_options_dict()
  269.  
  270.     def getOption(self, key):
  271.         opts = self.options
  272.         
  273.         if key == 'optlist':
  274.             return opts.keys()
  275.         
  276.         if key == 'area':
  277.             return (opts["tl-x"], opts["tl-y"]), (opts["br-x"], opts["br-y"])
  278.         
  279.         if key not in opts:
  280.             raise AttributeError, 'No such attribute: %s' % key
  281.         
  282.         opt = opts[key]
  283.         
  284.         if opt.type == scanext.TYPE_BUTTON:
  285.             raise AttributeError, "Buttons don't have values: %s" % key
  286.         
  287.         if opt.type == scanext.TYPE_GROUP:
  288.             raise AttributeError, "Groups don't have values: %s " % key
  289.         
  290.         if not scanext.isOptionActive(opt.cap):
  291.             raise AttributeError, 'Inactive option: %s' % key
  292.         
  293.         return self.dev.getOption(opt.index)
  294.  
  295.     def getOptionObj(self, key):
  296.         opts = self.options
  297.         if key in opts:
  298.             return opts[key]
  299.         
  300.  
  301.     def getParameters(self):
  302.         """Return a 6-tuple holding all the current device settings:
  303.            (format, format_name, last_frame, (pixels_per_line, lines), depth, bytes_per_line)
  304.         
  305.             - format is the SANE frame type
  306.             - format is one of 'grey', 'color' (RGB), 'red', 'green', 'blue'.
  307.             - last_frame [bool] indicates if this is the last frame of a multi frame image
  308.             - (pixels_per_line, lines) specifies the size of the scanned image (x,y)
  309.             - lines denotes the number of scanlines per frame
  310.             - depth gives number of pixels per sample
  311.         """
  312.         return self.dev.getParameters()
  313.  
  314.     def getOptions(self):
  315.         "Return a list of tuples describing all the available options"
  316.         return self.dev.getOptions()
  317.         
  318.     def startScan(self, byte_format='BGRA', update_queue=None, event_queue=None):
  319.         """
  320.             Perform a scan with the current device.
  321.             Calls sane_start().
  322.         """
  323.         if not self.isScanActive():
  324.             status = self.dev.startScan()
  325.             self.format, self.format_name, self.last_frame, self.pixels_per_line, \
  326.             self.lines, self.depth, self.bytes_per_line = self.dev.getParameters()
  327.             
  328.             self.scan_thread = ScanThread(self.dev, byte_format, update_queue, event_queue)
  329.             self.scan_thread.scan_active = True
  330.             self.scan_thread.start()
  331.             return True, self.lines * self.bytes_per_line, status
  332.         else:
  333.             # Already active
  334.             return False, 0, scanext.SANE_STATUS_DEVICE_BUSY
  335.             
  336.     def cancelScan(self):
  337.         "Cancel an in-progress scanning operation."
  338.         return self.dev.cancelScan()
  339.  
  340.     def getScan(self):
  341.         "Get the output buffer and info about a completed scan."
  342.         if not self.isScanActive():
  343.             s = self.scan_thread
  344.             
  345.             return s.buffer, s.format, s.format_name, s.pixels_per_line, \
  346.                 s.lines, s.depth, s.bytes_per_line, s.pad_bytes, s.total_read
  347.                 
  348.     def freeScan(self):
  349.         "Cleanup the scan file after a completed scan."
  350.         if not self.isScanActive():
  351.             s = self.scan_thread
  352.             
  353.             try:
  354.                 s.buffer.close()
  355.                 os.remove(s.buffer_path)
  356.             except (IOError, AttributeError):
  357.                 pass
  358.         
  359.     def isScanActive(self):
  360.         if self.scan_thread is not None:
  361.             return self.scan_thread.isAlive() and self.scan_thread.scan_active
  362.         else:
  363.             return False
  364.             
  365.     def waitForScanDone(self):
  366.         if self.scan_thread is not None and \
  367.             self.scan_thread.isAlive() and \
  368.             self.scan_thread.scan_active:
  369.  
  370.             try:
  371.                 self.scan_thread.join()
  372.             except KeyboardInterrupt:
  373.                 pass
  374.                 
  375.     def waitForScanActive(self):
  376.         time.sleep(0.5)
  377.         if self.scan_thread is not None:
  378.             while True:
  379.                 #print self.scan_thread.isAlive()
  380.                 #print self.scan_thread.scan_active
  381.                 if self.scan_thread.isAlive() and \
  382.                     self.scan_thread.scan_active:
  383.                     return
  384.                 
  385.                 time.sleep(0.5)
  386.                 #print "Waiting..."
  387.         
  388. ##    def scanMulti(self):
  389. ##        return _SaneIterator(self)
  390.         
  391.     def closeScan(self):
  392.         "Close the SANE device after a scan."
  393.         self.dev.closeScan()
  394.  
  395.         
  396. class ScanThread(threading.Thread):
  397.     def __init__(self, device, byte_format='BGRA', update_queue=None, event_queue=None):
  398.         threading.Thread.__init__(self)
  399.         self.scan_active = True
  400.         self.dev = device
  401.         self.update_queue = update_queue
  402.         self.event_queue = event_queue
  403.         self.buffer_fd, self.buffer_path = utils.make_temp_file(prefix='hpscan')
  404.         self.buffer = os.fdopen(self.buffer_fd, "w+b")
  405.         self.format = -1
  406.         self.format_name = ''
  407.         self.last_frame = -1
  408.         self.pixels_per_line = -1
  409.         self.lines = -1
  410.         self.depth = -1
  411.         self.bytes_per_line = -1
  412.         self.pad_bytes = -1
  413.         self.total_read = 0
  414.         self.byte_format = byte_format
  415.         
  416.         
  417.     def updateQueue(self, status, bytes_read):
  418.         if self.update_queue is not None:
  419.             try:
  420.                 status = int(status)
  421.             except (ValueError, TypeError):
  422.                 status = -1 #scanext.SANE_STATUS_GOOD
  423.                 
  424.             self.update_queue.put((status, bytes_read))
  425.             time.sleep(0)
  426.  
  427.     def run(self):
  428.         #self.scan_active = True
  429.         self.format, self.format_name, self.last_frame, self.pixels_per_line, \
  430.             self.lines, self.depth, self.bytes_per_line = self.dev.getParameters()
  431.             
  432.         log.debug("format=%d" % self.format)
  433.         log.debug("format_name=%s" % self.format_name)
  434.         log.debug("last_frame=%d" % self.last_frame)
  435.         log.debug("ppl=%d" % self.pixels_per_line)
  436.         log.debug("lines=%d" % self.lines)
  437.         log.debug("depth=%d" % self.depth)
  438.         log.debug("bpl=%d" % self.bytes_per_line)
  439.         log.debug("byte_format=%s" % self.byte_format)
  440.             
  441.         w = self.buffer.write
  442.         
  443.         if self.format == scanext.FRAME_RGB: # "Color"
  444.             if self.depth == 8: # 8 bpp (32bit)
  445.                 self.pad_bytes = self.bytes_per_line - 3 * self.pixels_per_line
  446.                 
  447.                 log.debug("pad_bytes=%d" % self.pad_bytes)
  448.                 
  449.                 dir = -1
  450.                 if self.byte_format == 'RGBA':
  451.                     dir = 1
  452.                 
  453.                 try:
  454.                     st, t = self.dev.readScan(self.bytes_per_line)
  455.                 except scanext.error, st:
  456.                     self.updateQueue(st, 0)
  457.                 
  458.                 #print st
  459.                 while st == scanext.SANE_STATUS_GOOD:
  460.                     
  461.                     if t:
  462.                         index = 0
  463.                         while index < len(t) - self.pad_bytes:
  464.                             w(t[index:index+3:dir])
  465.                             w('\xff')
  466.                             index += 3
  467.             
  468.                         self.total_read += len(t)
  469.                         self.updateQueue(st, self.total_read)
  470.                         log.debug("Read %d bytes" % self.total_read)
  471.                     
  472.                     else:
  473.                         time.sleep(0.1)
  474.                     
  475.                     try:
  476.                         st, t = self.dev.readScan(self.bytes_per_line)
  477.                     except scanext.error, st:
  478.                         self.updateQueue(st, self.total_read)
  479.                         break
  480.                     
  481.                     if self.checkCancel():
  482.                         break
  483.         
  484.         elif self.format == scanext.FRAME_GRAY: 
  485.         
  486.             if self.depth == 1: # 1 bpp lineart
  487.                 self.pad_bytes = self.bytes_per_line - (self.pixels_per_line + 7) // 8;
  488.                 
  489.                 log.debug("pad_bytes=%d" % self.pad_bytes)
  490.                 
  491.                 try:
  492.                     st, t = self.dev.readScan(self.bytes_per_line)
  493.                 except scanext.error, st:
  494.                     self.updateQueue(st, 0)
  495.                
  496.                 while st == scanext.SANE_STATUS_GOOD:
  497.                     
  498.                     if t:
  499.                         index = 0
  500.                         while index < len(t) - self.pad_bytes:
  501.                             k = 0x80
  502.                             j = ord(t[index])
  503.                             
  504.                             for b in range(8):
  505.                                 if k & j:
  506.                                     w("\x00\x00\x00\xff")
  507.                                 else:
  508.                                     w("\xff\xff\xff\xff")
  509.                                 
  510.                                 k = k >> 1
  511.                             
  512.                             index += 1
  513.                     
  514.                         self.total_read += len(t)
  515.                         self.updateQueue(st, self.total_read)
  516.                         log.debug("Read %d bytes" % self.total_read)
  517.                     else:
  518.                         time.sleep(0.1)
  519.                         
  520.                     try:
  521.                         st, t = self.dev.readScan(self.bytes_per_line)
  522.                     except scanext.error, st:
  523.                         self.updateQueue(st, self.total_read)
  524.                         break
  525.                 
  526.                     if self.checkCancel():
  527.                         break
  528.                     
  529.             elif self.depth == 8: # 8 bpp grayscale
  530.                 self.pad_bytes = self.bytes_per_line - self.pixels_per_line
  531.                 
  532.                 log.debug("pad_bytes=%d" % self.pad_bytes)
  533.                 
  534.                 try:
  535.                     st, t = self.dev.readScan(self.bytes_per_line)
  536.                 except scanext.error, st:
  537.                     self.updateQueue(st, 0)
  538.                 
  539.                 while st == scanext.SANE_STATUS_GOOD:
  540.                     
  541.                     if t:
  542.                         index = 0
  543.                         while index < len(t) - self.pad_bytes:
  544.                             j = t[index]
  545.                             w(j)
  546.                             w(j)
  547.                             w(j)
  548.                             w("\xff")
  549.                             
  550.                             index += 1
  551.                     
  552.                         self.total_read += len(t)
  553.                         self.updateQueue(st, self.total_read)
  554.                         log.debug("Read %d bytes" % self.total_read)
  555.                     else:
  556.                         time.sleep(0.1)
  557.                         
  558.                     try:
  559.                         st, t = self.dev.readScan(self.bytes_per_line)
  560.                     except scanext.error, st:
  561.                         self.updateQueue(st, self.total_read)
  562.                         break
  563.                     
  564.                     if self.checkCancel():
  565.                         break
  566.                     
  567.         #self.dev.cancelScan()
  568.         self.buffer.seek(0)
  569.         self.scan_active = False
  570.         log.debug("Scan thread exiting...")
  571.         
  572.     def checkCancel(self):
  573.         canceled = False
  574.         while self.event_queue.qsize():
  575.             try:
  576.                 event = self.event_queue.get(0)
  577.                 if event == EVENT_SCAN_CANCELED:
  578.                     canceled = True
  579.                     log.debug("Cancel pressed!")
  580.                     self.dev.cancelScan()
  581.             
  582.             except Queue.Empty:
  583.                 break
  584.  
  585.         return canceled
  586.         
  587.  
  588. def init():
  589.     return scanext.init()
  590.     
  591. def deInit():
  592.     return scanext.deInit()
  593.     
  594. def openDevice(dev):
  595.     "Open a device for scanning"
  596.     return ScanDevice(dev)
  597.     
  598. def getDevices(local_only=0):
  599.     return scanext.getDevices(local_only)
  600.     
  601.     
  602.